package tungsten.pb;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import org.apache.log4j.Logger;
import tungsten.base.server.BaseClient;
import tungsten.base.utils.ByteUtils;
import tungsten.base.utils.ClassUtils;
import tungsten.pb.module.Module;
import tungsten.pb.protocol.Heads;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@ChannelHandler.Sharable
public abstract class PBDecoder extends ChannelInboundHandlerAdapter
{
    private static Logger logger = Logger.getLogger(PBDecoder.class);

    private Map<Integer,Module> moduleMap = new HashMap<>();
    private Map<ChannelHandlerContext,BaseClient> clientMap = new ConcurrentHashMap<>();

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
    {

        BaseClient client = clientMap.get(ctx);
        if(client == null)
        {
            logger.error("can't find the client:" + ctx);
            return;
        }

        ByteBuf buf = (ByteBuf) msg;

        final byte[] array;
        final int length = buf.readableBytes();

        int offset;
        if (buf.hasArray())
        {
            array = buf.array();
            offset = buf.arrayOffset() + buf.readerIndex();
        }
        else
        {
            array = new byte[length];
            buf.getBytes(buf.readerIndex(), array, 0, length);
            offset = 0;
        }

        Heads.Head head = Heads.Head.getDefaultInstance().getParserForType().parseFrom(array,offset,length);

        Module module = moduleMap.get(head.getModuleId());

        if(module != null)
        {
            if(ByteUtils.crc32(head.getBytes().toByteArray()) != head.getCrc32())
            {
                logger.error("crc32 error");
                return;
            }
            module.onMessage(client,head);
        }
        else
        {
            logger.error("No module Id:" + head.getModuleId());
        }
    }

    /**
     * 注册消息派发的包
     * @param pkg
     */
    public void register(Package pkg)
    {
        moduleMap = new HashMap<>();
        List<Class<Module>> classes = ClassUtils.getClassList(PBAdapter.class.getPackage().getName(), true, PBAnnotation.class,null,false);

        for(Class<Module> module : classes)
        {
            PBAnnotation an = module.getAnnotation(PBAnnotation.class);
            try {
                moduleMap.put(an.module(),module.newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception
    {
        clientMap.put(ctx,getNewClient(ctx));
    }

    protected abstract BaseClient getNewClient(ChannelHandlerContext ctx);

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception
    {
        BaseClient client = clientMap.remove(ctx);
        if(client != null)
        {
            client.dispose();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
    {
        ctx.close();
    }
}
